package com.atguigu.gulimail.ware.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.To.OrderTo;
import com.atguigu.common.To.mq.StockDetailLockedTo;
import com.atguigu.common.To.mq.StockLockedTo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.gulimail.ware.dao.WareSkuDao;
import com.atguigu.gulimail.ware.entity.PurchaseDetailEntity;
import com.atguigu.gulimail.ware.entity.Vo.OrderItemVo;
import com.atguigu.gulimail.ware.entity.Vo.OrderVo;
import com.atguigu.gulimail.ware.entity.Vo.SkuStock;
import com.atguigu.gulimail.ware.entity.Vo.WareSkuLockVo;
import com.atguigu.gulimail.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimail.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimail.ware.entity.WareSkuEntity;
import com.atguigu.gulimail.ware.exception.NoStockException;
import com.atguigu.gulimail.ware.feign.OrderFeignService;
import com.atguigu.gulimail.ware.feign.ProductFeignService;
import com.atguigu.gulimail.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimail.ware.service.WareOrderTaskService;
import com.atguigu.gulimail.ware.service.WareSkuService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.core.context.RootContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareSkuDao wareSkuDao;

    @Autowired
    OrderFeignService orderFeignService;

    @Transactional
    @Override
    public void unlockStock(StockLockedTo stockLockedTo) {

        StockDetailLockedTo detail = stockLockedTo.getDetailLockedTo();
        Long taskId=detail.getTaskId();

        /**
         * 解锁
         * 1、查询数据库关于这个订单锁定库存信息
         *   有：证明库存锁定成功了
         *      解锁：订单状况
         *          1、没有这个订单，必须解锁库存 （订单服务回滚，但是库存锁定成功）
         *          2、有这个订单，不一定解锁库存
         *              订单状态：已取消：解锁库存
         *                      已支付：不能解锁库存
         */
        WareOrderTaskEntity task = wareOrderTaskService.getById(taskId);
        if(task!=null){
            String orderSn = task.getOrderSn();

            R r = orderFeignService.getBySn(orderSn);
            if(r.getCode()==0){
                //订单数据查询成功
                System.out.println("订单数据查询成功");

                String jsonString = JSON.toJSONString(r.get("data"));
                OrderVo orderVo = JSON.parseObject(jsonString, OrderVo.class);
                if(orderVo==null||orderVo.getStatus()==4){ //订单不存在或者订单状态为已取消---解锁
                    System.out.println("调用库存解锁");
                    Long detailId=detail.getId();
                    WareOrderTaskDetailEntity detailEntity = wareOrderTaskDetailService.getById(detailId);
                    if(detailEntity.getLockStatus()==1){
                        //当库存工作单详情状态为1：已锁定，才可以解锁
                        unLockStock(detailEntity.getSkuId(),detailEntity.getWareId(),detailEntity.getSkuNum(),detailId);
                    }
                }
            }else{
                //调取OrderServer失败，将消息重新放回队列等待别人消费
                System.out.println("调取OrderServer失败");
                throw new RuntimeException("调取OrderServer失败");
            }

        }else {
            //库存工作单不存在，无需操作
        }

    }

    @Transactional
    @Override
    public void unlockStock(OrderTo order) {
        String orderSn = order.getOrderSn();
        //如果order已经支付，不需要解锁
        if(order.getStatus()==1){
            System.out.println("订单已经支付，无需解锁");
            return;
        }
        //根据orderSn查询数据库，找到对应的工作单信息，根据工作单id查询工作单详情信息，并判断，工作单的的数据是否已经被解锁
        //已经被解锁就不需要重复解锁
        WareOrderTaskEntity wareOrderTaskEntity=wareOrderTaskService.getByOrderSn(orderSn);

        Long taskId=wareOrderTaskEntity.getId();
        List<WareOrderTaskDetailEntity> detailLists=wareOrderTaskDetailService.getByTaskId(taskId);
        for (WareOrderTaskDetailEntity detail:detailLists) {
            unLockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detail.getId());

        }
    }

    /**
     * 解锁库存的方法
     * @param skuId
     * @param wareId
     * @param num
     * @param taskDetailId
     */
    public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {
        System.out.println("库存解锁");

        //库存解锁
        wareSkuDao.unLockStock(skuId,wareId,num);

        //更新工作单的状态
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(taskDetailId);
        //变为已解锁
        taskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetailEntity);

    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryPageCondtion(Map<String, Object> params) {
        LambdaQueryWrapper<WareSkuEntity> wrapper=new LambdaQueryWrapper<>();

        String wareId=(String) params.get("wareId");
        String skuId=(String)params.get("skuId");

        wrapper.eq(StringUtils.isNotEmpty(wareId),WareSkuEntity::getWareId,wareId)
                .eq(StringUtils.isNotEmpty(skuId),WareSkuEntity::getSkuId,skuId);

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(PurchaseDetailEntity detailEntity) {
        Long skuId = detailEntity.getSkuId();
        Long wareId = detailEntity.getWareId();
        Integer skuNum = detailEntity.getSkuNum();

        LambdaQueryWrapper<WareSkuEntity> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(WareSkuEntity::getSkuId,skuId);
        wrapper.eq(WareSkuEntity::getWareId,wareId);

        WareSkuEntity one = this.getOne(wrapper);

        if (one!=null){
            Integer stock = one.getStock();
            one.setStock(stock+skuNum);
            this.updateById(one);
        }else {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            //远程查询name;
            R info = productFeignService.info(skuId);
           try {
               if (info.getCode() == 0) {
                   Map<String, Object> map =(Map<String,Object>) info.get("skuInfo");

                   wareSkuEntity.setSkuName((String) map.get("skuName"));

               }
           }catch (Exception e){

           }

            this.save(wareSkuEntity);
        }

    }

    @Override
    public List<SkuStock> getSkuStock(List<Long> skuIds) {

        List<SkuStock> collect = skuIds.stream().map(i -> {
            SkuStock vo = new SkuStock();
            //查询sku的库存总量-锁定库存总量
            Long count = baseMapper.getSkuStock(i);

            vo.setStock(count != null && count > 0);
            vo.setSkuId(i);
            log.info(vo.toString());

            return vo;
        }).collect(Collectors.toList());
        return collect;
    }

    @Transactional
    @Override
    public Boolean orderLocakStock(WareSkuLockVo wareSkuLockVo) {
        //实际在锁库存的时候，需要根据订单的收货地址，选择就近的仓库进行锁库存操作，此项目没有涉及
        log.info("Seata全局事务id=================>{}", RootContext.getXID());
        //在锁库存的过程中，将库存工作单记录在数据库（涉及到的sku，订单，仓库，锁库存的状态）
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(wareSkuLockVo.getOrderSn());
        wareOrderTaskEntity.setTaskStatus(1);
        wareOrderTaskService.save(wareOrderTaskEntity);

        //1. 找到每个商品在哪个仓库都有库存
        List<OrderItemVo> locks = wareSkuLockVo.getLocks();
        if(locks!=null && locks.size()>0){
            List<SkuWareHasStock> stocks = locks.stream().map(item -> {

                SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
                Long skuId = item.getSkuId();
                skuWareHasStock.setSkuId(skuId);
                skuWareHasStock.setNum(item.getCount());
                //根据SkuId查询ware_sku数据库，找到有库存的ware_id
                List<Long> wareIds= wareSkuDao.selectHasStockWareIds(skuId);
                skuWareHasStock.setWareId(wareIds);
                return skuWareHasStock;

            }).collect(Collectors.toList());

            //2. 锁定库存
            // 如果订单中的每一个商品都锁定成功，将当前商品锁定的信息发送给MQ
            // 如果锁定失败，该事务将会回滚，发送
            for (SkuWareHasStock stock:stocks) {
                Boolean skuLock=false;

                Long skuId = stock.getSkuId();//要锁的skuid
                Integer num = stock.getNum();//要锁的个数
                List<Long> wareIds = stock.getWareId();//要锁的仓库id

                //没有任何仓库有这个商品的库存
                if(wareIds==null||wareIds.size()==0){
                    System.out.println("没有任何仓库有这个商品的库存，skuid="+skuId.toString());
                    throw new RuntimeException(new NoStockException(skuId));
                }else {
                    //有库存时候，对每个ware进行锁库存操作
                    for (Long wareId:wareIds) {
                        Integer res = wareSkuDao.lockSkuStock(skuId, num, wareId);
                        if (res==1){
                            //说明锁库成功
                            skuLock=true;

                            //将锁定成功的库存工作详情单存储到数据库
                            WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                            wareOrderTaskDetailEntity.setSkuId(skuId);
                            wareOrderTaskDetailEntity.setWareId(wareId);
                            wareOrderTaskDetailEntity.setLockStatus(1);
                            wareOrderTaskDetailEntity.setSkuNum(num);
                            wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                            wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                            //TODO 发消息通知库存锁定成功；(哪一个库存工作单，锁了哪件商品，锁了多少件，对应订单号是多少)
                            StockLockedTo stockLockedTo = new StockLockedTo();
                            stockLockedTo.setId(wareOrderTaskEntity.getId());

                            StockDetailLockedTo detailLockedTo = new StockDetailLockedTo();
                            BeanUtils.copyProperties(wareOrderTaskDetailEntity,detailLockedTo);

                            stockLockedTo.setDetailLockedTo(detailLockedTo);

                            rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);
                            break;//跳出，锁下一个sku
                        }
                            //有仓库锁失败，可能原因是库存不足...
                            //继续循环重试下一个仓库
                    }
                    //当这个sku的所有仓库都没有锁成功时候
                    if(!skuLock){
                        System.out.println("没有货了");
                        throw new RuntimeException(new NoStockException(skuId));
                    }
                }
            }
        }


        return true;
    }


    @Data
    class SkuWareHasStock{
        private Long skuId;
        private Integer num;//锁的件数
        private List<Long> wareId;//有该商品库存，的仓库号
    }

}